(*
  Copyright (c) 2009 Barry Schwartz

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
*)

open Logging
  
(* counters *)
type counter = {
  mutable number : int;                 (* value of the counter *)
  mutable reset : counter list
}

(* counters which are reset everytime this counter changes *)
let make contents = { number = contents; reset = []; }
  
let rec set ctr contents =
  (ctr.number <- contents; List.iter reset ctr.reset)
and reset ctr = set ctr 0
  
let add_reset ctr sub_ctr =
  if List.memq sub_ctr ctr.reset
  then ()
  else ctr.reset <- sub_ctr :: ctr.reset
  
(* counter table *)
type counter_table = counter DynUCTrie.t

let empty_table = DynUCTrie.empty
  
let new_counter loc table name contents super =
  (if DynUCTrie.mem_string name table
   then
     (log_warn loc "counter \"";
      log_uc_string name;
      log_string "\" redefined!\n")
   else ();
   let new_ctr = make contents in
   let new_table = DynUCTrie.add_string name new_ctr table
   in
     ((match super with
       | None -> ()
       | Some ctr ->
           (try
              let c = DynUCTrie.find_string ctr new_table
              in add_reset c new_ctr
            with
            | Not_found ->
                (log_warn loc "counter \"";
                 log_uc_string name;
                 log_string "\" undefined!\n")));
      new_table))
  
let get_counter loc table name =
  try let c = DynUCTrie.find_string name table in c.number
  with
  | Not_found ->
      (log_warn loc "counter \"";
       log_uc_string name;
       log_string "\" undefined!\n";
       0)
  
let set_counter loc table name contents =
  try let c = DynUCTrie.find_string name table in (set c contents; table)
  with | Not_found -> new_counter loc table name contents None
  

